﻿using System.Collections.Generic;
using Jayden.Dll.Core;

namespace Jayden.Dll.Web
{
	public class UriManager : Dictionary<Uri, Uri>
	{
		private string m_DefaultFileName = "default.html";
		private string m_NotFoundUriString = "/404.html";
		private string m_NotAuthenticatedUriString = "/login.html";

		private Configuration m_Configuration;
		private List<UriRewriter> m_Rewriters = new List<UriRewriter>();
		private Dictionary<string, List<Uri>> m_ByVirtualPath = new Dictionary<string, List<Uri>>();
		private Dictionary<string, UriAccess> m_Accesses = new Dictionary<string, UriAccess>();

		public UriManager(Configuration config, Uri baseUri)
		{
			if (baseUri == null && System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Request != null)
				baseUri = new Uri(System.Web.HttpContext.Current.Request.Url);
			m_Configuration = config;
			m_DefaultFileName = config.GetValue("@default", m_DefaultFileName);
			m_NotFoundUriString = config.GetValue("@not-found", m_NotFoundUriString);
			m_NotAuthenticatedUriString = config.GetValue("@not-authenticated", m_NotAuthenticatedUriString);
			foreach (Configuration urlConfig in config.GetConfigurations("Url"))
			{
				string virtualUri = urlConfig.GetValue("@virtual");
				string mappedUri = urlConfig.GetValue("@mapped");
				string authenticatedBool = urlConfig.GetValue("@authenticated");
				string rolesList = urlConfig.GetValue("@roles");
				bool? authenticated = null;
				string[] roles = null;
				if (authenticatedBool != null)
					authenticated = bool.Parse(authenticatedBool);
				if (rolesList != null)
					roles = rolesList.Split(',');
				UriAccess access = new UriAccess(authenticated, roles);
				Uri vUri = new Uri(baseUri, virtualUri, false);
				Uri mUri = new Uri(baseUri, mappedUri, false);
				foreach (Configuration paramConfig in urlConfig.GetConfigurations("Param"))
				{
					string name = paramConfig.GetValue("@name");
					string value = paramConfig.GetValue("@value");
					mUri[name] = value;
				}

				AddInternal(vUri, mUri);
				m_Accesses.Add(vUri.AbsolutePath, access);
			}
			foreach (Configuration regex in config.GetConfigurations("Regex"))
			{
				string pattern = regex.GetValue("@pattern");
				string uri = regex.GetValue("@uri");
				string path = regex.GetValue("@path");
				UriRewriter rewriter = new RegexUriRewriter(pattern, uri, path);
				m_Rewriters.Add(rewriter);
			}
			foreach (Configuration lang in config.GetConfigurations("Lang"))
			{
				string cultureCode = lang.GetValue("@lang");
				UriRewriter rewriter = new LangUriRewriter(cultureCode);
				m_Rewriters.Add(rewriter);
			}
		}

		private void AddInternal(Uri key, Uri value)
		{
			base.Add(key, value);
			string absolutePath = key.AbsolutePath;
			List<Uri> list;
			if (!m_ByVirtualPath.TryGetValue(absolutePath, out list))
			{
				list = new List<Uri>();
				m_ByVirtualPath.Add(absolutePath, list);
			}
			list.Add(key);
		}

		public string DefaultFileName { get { return m_DefaultFileName; } }
		public string NotFoundUriString { get { return m_NotFoundUriString; } }
		public string NotAuthenticatedUriString { get { return m_NotAuthenticatedUriString; } }

		public Uri Manage(Uri uri, out UriAccess access)
		{
			foreach (UriRewriter rewriter in m_Rewriters)
				uri = rewriter.RewriteUri(uri);
			access = null;
			List<Uri> uris;
			if (m_ByVirtualPath.TryGetValue(uri.AbsolutePath, out uris))
			{
				Uri bestUri = null;
				int bestUriLength = -1;
				foreach (Uri possibleUri in uris)
				{
					int uriLength = possibleUri.ToAbsoluteString(true).Length;
					if (uri.HasSameParameters(possibleUri) && uriLength > bestUriLength)
					{
						bestUri = possibleUri;
						bestUriLength = uriLength;
					}
				}
				m_Accesses.TryGetValue(uri.AbsolutePath, out access);
				if (bestUri != null)
				{
					uri = uri.Clone() as Uri;
					Uri result = new Uri(this[bestUri]);
					uri.MergeQuery(result);
					result.MergeQuery(uri);
					return result;
				}
			}
			return uri;
		}

		public Uri Virtualize(Uri uri)
		{
			Uri bestUri = null;
			int bestUriLength = -1;
			foreach (KeyValuePair<Uri, Uri> pair in this)
			{
				int uriLength = pair.Value.ToAbsoluteString(true).Length;
				if (pair.Value.HasSameParameters(uri) && uriLength > bestUriLength)
				{
					bestUri = pair.Key;
					bestUriLength = uriLength;
				}
			}
			if (bestUri != null)
				return bestUri.Clone();
			return uri;
		}
	}
}
